/*
 * Copyright (C) 2007 IsmAvatar <cmagicj@nni.com>
 * 
 * This file is part of Conflict Map Editor (CME).
 * 
 * CME is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * CME is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License (COPYING) for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.conflict.main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToolBar;

public class MapEditor extends JPanel implements ActionListener,KeyEventPostProcessor
	{
	private static final long serialVersionUID = 1L;
	public static final int TILE_WIDTH = 50, TILE_HEIGHT = 50;

	public JButton bNew, bOpen, bSave, bSaveAs, bAbout;
	public JTextField sDesc;
	public IntegerField sWidth, sHeight;
	public JTextField sZoomDisp;
	public JButton sZoomIn, sZoomOut;
	public int sZoom = 100;
	public JCheckBox sRect, sGrid, sDeleteUnder;
	public ColorSelect sColor;
	public JTabbedPane tabs;
	public EditorArea ea;
	public FileHandler fh;
	public TileChooser floor, object, ceiling;
	public JCheckBox fShow, oShow, cShow;
	public JCheckBox fDelSpace, fDelGray;

	public MapEditor()
		{
		super(new BorderLayout());
		add("North",makeToolBar());
		//		add("West",makeTabs());
		ea = new EditorArea(this);
		JScrollPane sp = new JScrollPane(ea);
		add("Center",sp);
		add("East",makeSettings());
		fh = new FileHandler(this);
		}

	private JPanel makeSettings()
		{
		JPanel panel = new JPanel(new FlowLayout());
		size(panel,200,200);

		panel.add(new JLabel("Description"));
		sDesc = new JTextField("New Map 1");
		add(panel,sDesc,180,20);
		panel.add(new JLabel("Width:"));
		sWidth = new IntegerField(15,60,20);
		sWidth.addActionListener(this);
		add(panel,sWidth,40,20);
		addGap(panel,80,1);
		panel.add(new JLabel("Height:"));
		sHeight = new IntegerField(15,60,20);
		sHeight.addActionListener(this);
		add(panel,sHeight,40,20);
		addGap(panel,80,1);
		sGrid = new JCheckBox("Show Grid",true);
		sGrid.addActionListener(this);
		add(panel,sGrid);
		sColor = new ColorSelect(Color.RED);
		sColor.addActionListener(this);
		add(panel,sColor);
		sRect = new JCheckBox("Allow Region Filling");
		add(panel,sRect);
		sDeleteUnder = new JCheckBox("Delete Underlying",true);
		sDeleteUnder.setHorizontalAlignment(JCheckBox.CENTER);
		sDeleteUnder.addActionListener(this);
		add(panel,sDeleteUnder,180,20);
		sZoomIn = new JButton(findIcon("actions/viewmag-.png"));
		sZoomIn.setToolTipText("Zoom In");
		sZoomIn.addActionListener(this);
		add(panel,sZoomIn,20,20);
		sZoomDisp = new JTextField(sZoom + "%",4);
		sZoomDisp.setEditable(false);
		add(panel,sZoomDisp);
		sZoomOut = new JButton(findIcon("actions/viewmag+.png"));
		sZoomOut.setToolTipText("Zoom Out");
		sZoomOut.addActionListener(this);
		add(panel,sZoomOut,20,20);

		panel.add(makeTabs());

		return panel;
		}

	private JToolBar makeToolBar()
		{
		JToolBar bar = new JToolBar();
		bar.setFloatable(false);
		bNew = new JButton(findIcon("actions/new.png"));
		bNew.setToolTipText("New Map");
		bNew.addActionListener(this);
		bar.add(bNew);
		bOpen = new JButton(findIcon("actions/open.png"));
		bOpen.setToolTipText("Open a Map");
		bOpen.addActionListener(this);
		bar.add(bOpen);
		bSave = new JButton(findIcon("actions/save.png"));
		bSave.setToolTipText("Save Map");
		bSave.addActionListener(this);
		bar.add(bSave);
		bar.addSeparator();
		bSaveAs = new JButton(findIcon("actions/save-as.png"));
		bSaveAs.setToolTipText("Save Map As...");
		bSaveAs.addActionListener(this);
		bar.add(bSaveAs);
		bar.addSeparator();
		bAbout = new JButton(findIcon("actions/about.png"));
		bAbout.setToolTipText("About CME");
		bAbout.addActionListener(this);
		bar.add(bAbout);
		return bar;
		}

	private JTabbedPane makeTabs()
		{
		tabs = new JTabbedPane();
		size(tabs,200,300);
		tabs.addTab("Floors",makeFloorTab());
		tabs.addTab("Objects",makeObjectTab());
		tabs.addTab("Ceilings",makeCeilingTab());
		return tabs;
		}

	private JPanel makeFloorTab()
		{
		JPanel panel = new JPanel(new FlowLayout());

		fShow = new JCheckBox("Show Floors",true);
		fShow.addActionListener(this);
		panel.add(fShow);
		floor = new TileChooser("floors/floor",2,1);
		size(floor,150,100);
		panel.add(floor);
		JLabel lc = new JLabel("[Left Clears]");
		lc.setOpaque(true);
		lc.setBackground(Color.YELLOW);
		lc.addMouseListener(new MouseListener()
			{
				public void mouseClicked(MouseEvent e)
					{
					if (e.getButton() == MouseEvent.BUTTON1) floor.setLeft(0);
					}

				public void mouseEntered(MouseEvent e)
					{
					}

				public void mouseExited(MouseEvent e)
					{
					}

				public void mousePressed(MouseEvent e)
					{
					}

				public void mouseReleased(MouseEvent e)
					{
					}
			});
		panel.add(lc);
		JLabel rc = new JLabel("[Right Clears]");
		rc.setOpaque(true);
		rc.setBackground(Color.YELLOW);
		rc.addMouseListener(new MouseListener()
			{
				public void mouseClicked(MouseEvent e)
					{
					if (e.getButton() == MouseEvent.BUTTON1) floor.setRight(0);
					if (e.getButton() == MouseEvent.BUTTON3) floor.setRight(0);
					}

				public void mouseEntered(MouseEvent e)
					{
					}

				public void mouseExited(MouseEvent e)
					{
					}

				public void mousePressed(MouseEvent e)
					{
					}

				public void mouseReleased(MouseEvent e)
					{
					}
			});
		panel.add(rc);
		fDelSpace = new JCheckBox("Delete Underlying Space",true);
		fDelSpace.setEnabled(false);
		panel.add(fDelSpace);
		fDelGray = new JCheckBox("Delete Underlying Gray",true);
		fDelGray.setEnabled(false);
		panel.add(fDelGray);

		return panel;
		}

	private JPanel makeObjectTab()
		{
		JPanel panel = new JPanel(new FlowLayout());

		oShow = new JCheckBox("Show Objects",true);
		oShow.addActionListener(this);
		panel.add(oShow);
		object = new TileChooser("objects/object",4,9);
		size(object,150,200);
		panel.add(object);
		JLabel lc = new JLabel("[Left Clears]");
		lc.setOpaque(true);
		lc.setBackground(Color.YELLOW);
		lc.addMouseListener(new MouseListener()
			{
				public void mouseClicked(MouseEvent e)
					{
					if (e.getButton() == MouseEvent.BUTTON1) object.setLeft(0);
					}

				public void mouseEntered(MouseEvent e)
					{
					}

				public void mouseExited(MouseEvent e)
					{
					}

				public void mousePressed(MouseEvent e)
					{
					}

				public void mouseReleased(MouseEvent e)
					{
					}
			});
		panel.add(lc);
		JLabel rc = new JLabel("[Right Clears]");
		rc.setOpaque(true);
		rc.setBackground(Color.YELLOW);
		rc.addMouseListener(new MouseListener()
			{
				public void mouseClicked(MouseEvent e)
					{
					if (e.getButton() == MouseEvent.BUTTON1) object.setRight(0);
					if (e.getButton() == MouseEvent.BUTTON3) object.setRight(0);
					}

				public void mouseEntered(MouseEvent e)
					{
					}

				public void mouseExited(MouseEvent e)
					{
					}

				public void mousePressed(MouseEvent e)
					{
					}

				public void mouseReleased(MouseEvent e)
					{
					}
			});
		panel.add(rc);

		return panel;
		}

	private JPanel makeCeilingTab()
		{
		JPanel panel = new JPanel(new FlowLayout());

		cShow = new JCheckBox("Show Ceilings",true);
		cShow.addActionListener(this);
		KeyboardFocusManager wtf = KeyboardFocusManager.getCurrentKeyboardFocusManager();
		wtf.addKeyEventPostProcessor(this);/*new KeyEventPostProcessor()
																	{
																		public boolean postProcessKeyEvent(KeyEvent e)
																			{
																			if (e.getID() == KeyEvent.KEY_PRESSED) keyPressed(e);
																			return false;
																			}
																	})*/
		;
		panel.add(cShow);
		ceiling = new TileChooser("ceilings/ceiling",3,5);
		size(ceiling,150,150);
		panel.add(ceiling);
		JLabel lc = new JLabel("[Left Clears]");
		lc.setOpaque(true);
		lc.setBackground(Color.YELLOW);
		lc.addMouseListener(new MouseListener()
			{
				public void mouseClicked(MouseEvent e)
					{
					if (e.getButton() == MouseEvent.BUTTON1) ceiling.setLeft(0);
					}

				public void mouseEntered(MouseEvent e)
					{
					}

				public void mouseExited(MouseEvent e)
					{
					}

				public void mousePressed(MouseEvent e)
					{
					}

				public void mouseReleased(MouseEvent e)
					{
					}
			});
		panel.add(lc);
		JLabel rc = new JLabel("[Right Clears]");
		rc.setOpaque(true);
		rc.setBackground(Color.YELLOW);
		rc.addMouseListener(new MouseListener()
			{
				public void mouseClicked(MouseEvent e)
					{
					if (e.getButton() == MouseEvent.BUTTON1) ceiling.setRight(0);
					if (e.getButton() == MouseEvent.BUTTON3) ceiling.setRight(0);
					}

				public void mouseEntered(MouseEvent e)
					{
					}

				public void mouseExited(MouseEvent e)
					{
					}

				public void mousePressed(MouseEvent e)
					{
					}

				public void mouseReleased(MouseEvent e)
					{
					}
			});
		panel.add(rc);
		panel.add(new JLabel(
				"<html>Keyboard Shortcuts for these<br>are 789 456 123<br>and qwe asd zxc</html>"));

		return panel;
		}

	/**
	 * Finds the icon for the given name relative to icons.
	 * @param filename The name of the file relative to icons.
	 * @return The icon that matches the given filename
	 */
	public static ImageIcon findIcon(String filename)
		{
		String location = "org/conflict/icons/" + filename;
		ImageIcon ico = new ImageIcon(location);
		if (ico.getIconWidth() == -1)
			{
			URL url = MapEditor.class.getClassLoader().getResource(location);
			if (url != null) ico = new ImageIcon(url);
			}
		return ico;
		}

	/**
	 * Finds the image for the given name relative to icons.
	 * @param filename The name of the file relative to icons.
	 * @return The image that matches the given filename, or null
	 */
	public static BufferedImage getImage(String filename)
		{
		BufferedImage img = null;
		try
			{
			URL url = MapEditor.class.getClassLoader().getResource("org/conflict/icons/" + filename);
			if (url != null) img = ImageIO.read(url);
			}
		catch (IOException e)
			{
			e.printStackTrace();
			}
		return img;
		}

	/** Convenience method to set the Preferred and Minimum size of a component 
	 * @param c The component
	 * @param w Desired Width
	 * @param h Desired Height
	 */
	public static void size(Component c, int w, int h)
		{
		c.setMinimumSize(new Dimension(w,h));
		c.setPreferredSize(new Dimension(w,h));
		}

	/** This method is provided as an easy alternative to add(p,c,w,h) */
	public static void add(JComponent p, JComponent c)
		{
		p.add(c);
		}

	/**
	 * Convenience method for adding a component of given size to the parent 
	 * @param p Parent Component
	 * @param c Component to be added
	 * @param w Desired Width
	 * @param h Desired Height
	 */
	public static void add(JComponent p, JComponent c, int w, int h)
		{
		size(c,w,h);
		p.add(c);
		}

	/** Add a gap (empty JLabel) of given size to the given container p - for esthetics */
	public static void addGap(JComponent p, int w, int h)
		{
		JLabel lab = new JLabel();
		size(lab,w,h);
		p.add(lab);
		}

	public static void main(String[] args)
		{
		JFrame frame = new JFrame("Conflict Map Editor");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setContentPane(new MapEditor());
		size(frame,750,574);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		}

	public void actionPerformed(ActionEvent e)
		{
		if (e.getSource() == bNew)
			{
			fh.newFile();
			return;
			}
		if (e.getSource() == bOpen)
			{
			fh.load();
			return;
			}
		if (e.getSource() == bSave)
			{
			fh.save();
			return;
			}
		if (e.getSource() == bSaveAs)
			{
			fh.saveAs();
			return;
			}
		if (e.getSource() == bAbout)
			{
			JOptionPane.showMessageDialog(null,"Conflict Map Editor (CME) version 0.1.0\n"
					+ "Copyright \u00A9 2007 IsmAvatar <cmagicj@nni.com>\n"
					+ "CME comes with ABSOLUTELY NO WARRANTY; For details see LICENSE.\n"
					+ "This is free software, and you are welcome to redistribute it under\n"
					+ "certain conditions; See COPYING for details.","About Conflict Map Editor (CME)",
					JOptionPane.INFORMATION_MESSAGE);
			}
		if (e.getSource() == sWidth || e.getSource() == sHeight || e.getSource() == sGrid
				|| e.getSource() == sColor || e.getSource() == fShow || e.getSource() == oShow
				|| e.getSource() == cShow)
			{
			ea.updateUI();
			return;
			}
		if (e.getSource() == sDeleteUnder)
			{
			if (sDeleteUnder.isSelected())
				{
				fDelSpace.setEnabled(false);
				fDelGray.setEnabled(false);
				}
			else
				{
				fDelSpace.setEnabled(true);
				fDelGray.setEnabled(true);
				}
			return;
			}
		if (e.getSource() == sZoomIn)
			{
			if (sZoom > 25) sZoom /= 2;
			sZoomDisp.setText(sZoom + "%");
			ea.updateUI();
			return;
			}
		if (e.getSource() == sZoomOut)
			{
			if (sZoom < 200) sZoom *= 2;
			sZoomDisp.setText(sZoom + "%");
			ea.updateUI();
			return;
			}
		}

	public boolean postProcessKeyEvent(KeyEvent e)
		{
		if (e.getKeyCode() == KeyEvent.VK_SHIFT)
			{
			if (e.getID() == KeyEvent.KEY_PRESSED)
				sRect.setSelected(true);
			else if (e.getID() == KeyEvent.KEY_RELEASED)
				sRect.setSelected(false);
			else
				return false;
			return true;
			}
		if (e.getID() != KeyEvent.KEY_PRESSED) return false;
		//umm... Yeah... This was fun to type out....
		int p = 0;
		switch (e.getKeyChar())
			{
			case '+':
				actionPerformed(new ActionEvent(sZoomOut,0,null));
				return true;
			case '-':
				actionPerformed(new ActionEvent(sZoomIn,0,null));
				return true;
			case 'g':
				sGrid.setSelected(!sGrid.isSelected());
				ea.updateUI();
				return true;
			case '7':
			case 'q':
				p = 1;
				break;
			case '8':
			case 'w':
				p = 2;
				break;
			case '9':
			case 'e':
				p = 3;
				break;
			case '4':
			case 'a':
				p = 4;
				break;
			case '5':
			case 's':
				p = 5;
				break;
			case '6':
			case 'd':
				p = 6;
				break;
			case '1':
			case 'z':
				p = 7;
				break;
			case '2':
			case 'x':
				p = 8;
				break;
			case '3':
			case 'c':
				p = 9;
				break;
			}
		if (p == 0) return false;
		if (tabs.getSelectedIndex() == 0) //floors
			{
			//I figured I'd let them use 123 or qwe for top row. Might as well.
			if (e.getKeyChar() == '1' || e.getKeyChar() == '2' || e.getKeyChar() == '3') p -= 6;
			if (p > 6) return true;
			floor.setLeft(p);
			return true;
			}
		if (tabs.getSelectedIndex() == 1) //objects
			{
			p += 3;
			if (e.getKeyChar() == '1' || e.getKeyChar() == '2' || e.getKeyChar() == '3') p -= 9;
			//789 and 456 aren't intended for use with objects, but they work anyways,
			//since I figured it'd just be extra effort to filter them out.
			object.setLeft(p);
			return true;
			}
		ceiling.setLeft(p);
		return true;
		}
	}
